<?php
/*
 *	bbcode[for]xhtml - PHP bbcode parser.
 *
 *	bbcodexhtml.php (1.3)
 *      
 *	Copyright 2008 Raphael Michel <webmaster@raphaelmichel.de>
 *      
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU Lesser General Public License as 
 *	published by the Free Software Foundation; either version 3 of the License, 
 *	or (at your option) any later version.
 *		
 *	This program is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU Lesser General Public License for more details.
 *      
 *	You should have received a copy of the GNU Lesser General Public License
 *	along with this program; if not, visit http://www.gnu.org/licenses/lgpl.html
 *		
 *	CHANGELOG
 *	
 *	v1.3
 *	* block elements like <pre> mustn't be inside <p>
 *	v1.2
 *	* security module: Image-URLs must end .(png|gif|jpe*g|bmp|tiff|svg)
 *	* semantic [quote="author"]qouter[/quote] => <cite>author:</cite><blockquote>quote</blockquote>
 *	v1.1
 *	* typographic bugs fixed
 *	v1.0
 *	* project created
 *		
 *	COMING [maybee] SOON:
 *	* Syntax-Hightlighting with GeSHi
 */

/*
 *	Usage:
 *	$bbcode = new bbcodexhtml;
 *	$bbcode->disable_tag("img"); //for example: Disables images
 *	echo $bbcode->parse($text);
 * 
 */



class bbcodexhtml {
	var $tags = array(
									"url" => true,
									"img" => true,
									"b" => true,
									"u" => true,
									"i" => true,
									"quote" => true,
									"code" => true,
									"center" => true,
									"color" => true,
									"size" => true,
									"list" => true
							);      
	var $security = true;
			
	/* bool enable_tag ( string $tag ) */           
	function enable_tag($tag){
		if(isset($this->tags[$tag])) $this->tags[$tag] = true;
		else return false;
		return true;    
	}       
	
	/* bool disable_tag ( string $tag ) */
	function disable_tag($tag){
		if(isset($this->tags[$tag])) $this->tags[$tag] = false;
		else return false;
		return true;    
	}
	
	/* bool disable_security ( ) */
	function disable_security(){
		$this->security = false;
	}
	
	/* bool enable_security ( ) */
	function enable_security(){
		$this->security = true;
	}


	/* string parse ( string $text [, $parse_newlines = true ] ) */
	function parse($text, $parse_newlines = true){
	
		$this->pnl = $parse_newlines;
		
		$text = htmlspecialchars($text);
		
		//Parse code
		if($this->tags["code"] == true){
			 $text = preg_replace_callback("#\\[code\\](.+)\\[/code\\]#iU", array($this, '_parsecode'), $text);
		}
		
		//Parse url
		if($this->tags["url"] == true){
			$text = preg_replace("#\\[url\\]((http|https|ftp|irc|mailto|skype):[/]*[^ \n]+)\\[/url\\]#isU", "<a href=\"\\1\">\\1</a>", $text);
			$text = preg_replace("#\\[url=((http|https|ftp|irc|mailto|skype):[/]*[^ \n]+)\\](.+)\\[/url\\]#isU", "<a href=\"\\1\">\\3</a>", $text);
		}
		
		//Parse img
		if($this->tags["img"] == true && !$this->security) $text = preg_replace("#\\[img\\]((http|https|ftp|irc|mailto|skype):[/]*[^ \n]+)\\[/img\\]#isU", "<img src=\"\\1\" alt=\"Image\" />", $text);
		elseif($this->tags["img"] == true && $this->security) $text = preg_replace("#\\[img\\]((http|https|ftp|irc|mailto|skype):[/]*[^ \n]+\\.(png|gif|jpg|jpeg|bmp|tiff|svg))\\[/img\\]#isU", "<img src=\"\\1\" alt=\"Image\" />", $text);
		
		//Parse b
		if($this->tags["b"] == true) $text = preg_replace("#\\[b\\](.+)\\[/b\\]#isU", "<strong>\\1</strong>", $text);
		
		//Parse i
		if($this->tags["i"] == true) $text = preg_replace("#\\[i\\](.+)\\[/i\\]#isU", "<em>\\1</em>", $text);
		
		//Parse u
		if($this->tags["u"] == true) $text = preg_replace("#\\[u\\](.+)\\[/u\\]#isU", "<span style=\"text-decoration: underline;\">\\1</span>", $text);
		
		//Parse quote
		if($this->tags["quote"] == true){
			$text = preg_replace("#\\[quote\\](.+)\\[/quote\\]#isU", ($this->pnl ? '</p>' : '')."<blockquote>\\1</blockquote>".($this->pnl ? '<p>' : ''), $text);
			$text = preg_replace("#\\[quote=([^\\]]+)\\](.+)\\[/quote\\]#isU", "<cite>\\1</cite>:<blockquote>\\2</blockquote>", $text);
		}
		
		//Parse center
		if($this->tags["center"] == true) $text = preg_replace("#\\[center\\](.+)\\[/center\\]#isU", "<div style=\"text-align: center;\">\\1</div>", $text);
		
		//Parse color
		if($this->tags["color"] == true) $text = preg_replace("~\\[color=(#[a-z0-9]+|rgb\\([0-9]+,[0-9]+,[0-9]+\\)|[a-z]+)\\](.+)\\[/color\\]~isU", "<span style=\"color: \\1\">\\2</span>", $text);
		
		//Parse size
		if($this->tags["size"] == true) $text = preg_replace_callback("#\\[size=([0-9]+)\\](.+)\\[/size\\]#isU", array($this, '_parsesize'), $text);
		
		//Parse list
		if($this->tags["list"] == true){
			$text = preg_replace_callback("#\\[list\\](.+)\\[/list\\]#isU", array($this, '_parseulist'), $text);
			$text = preg_replace_callback("#\\[list=1\\](.+)\\[/list\\]#isU", array($this, '_parseolist'), $text);
		}
		
		if($parse_newlines){
			$text = "<p>".$text."</p>";
			$text = preg_replace("#\n\n#", "</p><p>", $text);
			#$text = nl2br($text);´
			$text = preg_replace("#<p>[ 	\r\n]*</p>#", "", $text);
		}
		
		return $text;
	}
	
	/* internal functions */
	function _parsecode($matches){
		return
			($this->pnl ? '</p>' : '')."<pre><code>".str_replace("[","&#91;",
			str_replace("]","&#93;",
			$matches[1]))."</pre></code>".($this->pnl ? '<p>' : '');
	}
	function _parsesize($matches){
		if($matches[1] > 30) $matches[1] = 30;
		$size = ($matches[1]*10)."%";
		return "<span style=\"font-size: $size;\">{$matches[2]}</span>";
	}
	function _parseulist($matches){
		$matches[1] = preg_replace("#\\[\\*\\](.*)\n#iU","<li>\\1</li>",$matches[1]);
		return trim(($this->pnl ? '</p>' : '')."<ul>".trim($matches[1])."</ul>".($this->pnl ? '<p>' : ''));
	}
	function _parseolist($matches){
		$matches[1] = preg_replace("#\\[\\*\\](.*)\n#iU","<li>\\1</li>",$matches[1]);
		return trim(($this->pnl ? '</p>' : '')."<ol>".trim($matches[1])."</ol>".($this->pnl ? '<p>' : ''));
	}
}

